﻿using System;
using System.Collections.Generic;
using System.Linq;

using PostSharp.Laos;
using System.Reflection;
using log4net;

namespace PostSharpCache
{
    [Serializable]
    [global::System.AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
    public class CacheAttribute : OnMethodBoundaryAspect
    {
        /// <summary>
        /// 日记记录器
        /// </summary>
        //private static readonly ILog _log = LogManager.GetLogger(typeof(CacheAttribute));
        private static readonly ILog _log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

        /// <summary>
        /// 用来生成的key值，key值中包含方法名、参数值等，因此参数不一时，方法会被执行
        /// </summary>
        private string formatStrings = "{0}、{1}、{2}";

        /// <summary>
        /// 用于缓存
        /// </summary>
        private static readonly Dictionary<string, object> cache = new Dictionary<string, object>();
        /// <summary>
        /// 编译时对方法的验证
        /// </summary>
        /// <param name="method">提供有关方法和构造函数的信息</param>
        /// <returns></returns>
        public override bool CompileTimeValidate(MethodBase method)
        {
            //try
            //{
                if (method is ConstructorInfo)
                {
                    _log.Debug("不能为构造函数创建缓存！");
                    return false;
                }

                MethodInfo methodInfo = (MethodInfo)method;

                if (methodInfo.ReturnType.Name == "Void")
                {
                    _log.Debug("不能为返回空的方法创建缓存！");
                    return false;
                }

                //查找方法参数中带 out 标记的参数个数
                int outparam = method.GetParameters().Count(e => e.IsOut);
                if (outparam > 0)
                {
                    _log.Debug("不支持 out 参数！");
                    return false;
                }
            //}
            //catch (Exception e)
            //{
            //    _log.Debug(e.Message);
            //    return false;
            //}

            return true;
        }

        /// <summary>
        /// 编译时刻 初始化
        /// </summary>
        /// <param name="method"></param>
        public override void CompileTimeInitialize(MethodBase method)
        {
            this.formatStrings = string.Format(formatStrings, "arg0", "arg1", "arg2");
        }

        /// <summary>
        /// 进入方法体时
        /// </summary>
        /// <param name="eventArgs"></param>
        public override void OnEntry(MethodExecutionEventArgs eventArgs)
        {
            string key = string.Format(this.formatStrings, eventArgs.Instance, eventArgs.Method, eventArgs.GetArguments());

            lock (cache)
            {
                object value;

                //查看是否存在缓存
                if (!cache.TryGetValue(key, out value))
                {
                    //缓存不存在，继续执行这个方法，并将key存在MethodExecutionTag，在执行完毕的
                    //OnSuccess事件时使用key值将结果放到缓存中
                    eventArgs.MethodExecutionTag = key;
                }
                else
                {
                    //已经在缓存中存在，则将执行的返回值直接设置为缓存中的值，不执行方法体而立即返回
                    eventArgs.ReturnValue = value;
                    eventArgs.FlowBehavior = FlowBehavior.Return;
                }
            }
        }

        /// <summary>
        /// 方法被成功执行时触发
        /// </summary>
        /// <param name="eventArgs"></param>
        public override void OnSuccess(MethodExecutionEventArgs eventArgs)
        {
            //取得缓存的key值
            string key = (string)eventArgs.MethodExecutionTag;
            //把执行结果放入缓存中
            lock (cache)
            {
                cache[key] = eventArgs.ReturnValue;
            }
        }
    }
}
